package com.zch.rpc.core.invoke;

import com.zch.rpc.core.annotation.FeignService;
import com.zch.rpc.core.enums.ProtocolEnum;
import com.zch.rpc.core.exception.RpcException;
import com.zch.rpc.core.model.RpcRequestModel;
import com.zch.rpc.core.model.RpcResponseModel;
import com.zch.rpc.core.model.FeignServiceMethodInfo;
import com.zch.rpc.core.net.jetty1.JettyClient;
import com.zch.rpc.core.register.RegisterService;
import com.zch.rpc.core.serialize.Serializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Auther: zch
 * @Date: 2018/12/28 17:16
 * @Description:
 */
public class RpcReferenceBean implements InvocationHandler {
    private Logger logger = LoggerFactory.getLogger(RpcReferenceBean.class);
    private Class<?> interfaceClass;
    private Serializer serializer;
    private String serverIpPort;
    private Class<? extends RegisterService> registerService;
    private String version;
    private String groupId;
    private JettyClient client;
    private String protocol;

    public Object getReference() {
        return Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, this);
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String className = method.getDeclaringClass().getName();

        // filter method like "Object.toString()"
        if (Object.class.getName().equals(className)) {
            //logger.info(">>>>>>>>>>> xxl-rpc proxy class-method not support [{}.{}]", className, method.getName());
            throw new RpcException("rpc proxy class-method not support");
        }
        RpcRequestModel requestModel = new RpcRequestModel();
        if (StringUtils.isEmpty(protocol)) {
            requestModel.setRequestId(UUID.randomUUID().toString());
            requestModel.setVersion(version);
            requestModel.setGroupId(groupId);
            requestModel.setParams(args);
            requestModel.setMethodName(method.getName());
            requestModel.setParamTypes(method.getParameterTypes());
            requestModel.setInterfaceClass(interfaceClass);
            RpcResponseModel responseModel = client.send(requestModel, serverIpPort);
            if (responseModel.getData() != null) {
                return responseModel.getData();
            }
        } else if (protocol.equals(ProtocolEnum.RESTFUL.getValue())) {
            FeignServiceMethodInfo feignServiceMethodInfo = getFeignServiceMethodInfo(method);

            if (feignServiceMethodInfo != null) {
                requestModel.setMappingUrl(feignServiceMethodInfo.getMappingUrl());
                requestModel.setRequestId(UUID.randomUUID().toString());
                requestModel.setParams(args);
                requestModel.setMethodName(method.getName());
                requestModel.setParamTypes(method.getParameterTypes());
                RpcResponseModel responseModel = client.send(requestModel, serverIpPort);
                return responseModel.getData();
            } else {
                //logger.error(">>>>>>>>>>> rpc proxy class-method not support [{}.{}]", className, method.getName());
                throw new RpcException("rpc proxy class-method not support");
            }
        }

        return null;
    }

    public RpcReferenceBean(Class<?> interfaceClass, Serializer serializer, String serverIpPort, Class<? extends RegisterService> registerService, String version, String groupId, String protocol) {
        this.interfaceClass = interfaceClass;
        this.serializer = serializer;
        this.serverIpPort = serverIpPort;
        this.registerService = registerService;
        this.version = version;
        this.groupId = groupId;
        this.protocol = protocol;
        this.client = new JettyClient();
    }

    private ConcurrentHashMap<String, FeignServiceMethodInfo> feignServiceMethodInfoConcurrentHashMap = new ConcurrentHashMap<>();

    private FeignServiceMethodInfo getFeignServiceMethodInfo(Method method) {

        String methodInfo = method.getDeclaringClass().getName() + "." + method.getName();
        FeignServiceMethodInfo feignServiceMethodInfo = feignServiceMethodInfoConcurrentHashMap.get(methodInfo);
        if (feignServiceMethodInfo == null) {
            FeignService feignService = method.getDeclaringClass().getAnnotation(FeignService.class);
            FeignService methodFeginService = method.getAnnotation(FeignService.class);
            feignServiceMethodInfo = new FeignServiceMethodInfo();
            feignServiceMethodInfo.setServerName(feignService.name());
            feignServiceMethodInfo.setMappingUrl(methodFeginService.path());
            feignServiceMethodInfoConcurrentHashMap.put(methodInfo, feignServiceMethodInfo);
        }
        return feignServiceMethodInfo;
    }

    public Class<?> getInterfaceClass() {
        return interfaceClass;
    }

    public void setInterfaceClass(Class<?> interfaceClass) {
        this.interfaceClass = interfaceClass;
    }

    public Serializer getSerializer() {
        return serializer;
    }

    public void setSerializer(Serializer serializer) {
        this.serializer = serializer;
    }

    public String getServerIpPort() {
        return serverIpPort;
    }

    public void setServerIpPort(String serverIpPort) {
        this.serverIpPort = serverIpPort;
    }

    public Class<? extends RegisterService> getRegisterService() {
        return registerService;
    }

    public void setRegisterService(Class<? extends RegisterService> registerService) {
        this.registerService = registerService;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public String getGroupId() {
        return groupId;
    }

    public void setGroupId(String groupId) {
        this.groupId = groupId;
    }
}
